package com.project.thisDatabase.base.db;

import com.google.gson.reflect.TypeToken;
import com.project.thisCommon.common.ConvertEntity;
import com.project.thisDatabase.base.DbExecutorBase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.*;
import java.util.function.Function;

@Component
public class DbExecutor implements DbExecutorBase {

    @Autowired(required = false)
    private DataSource dataSource;

    @Override
    public <T> List<T> search(String sql, List params, Class cls) throws SQLException {
        return innerSearch(sql, params, row->ConvertEntity.jsonToCopy(row, cls));
    }

    @Override
    public <T> List<T> search(String sql, List params, Type tp) throws SQLException {
        return innerSearch(sql, params, row->ConvertEntity.jsonToCopy(row, tp));
    }

    @Override
    public int update(String sql, List params) throws SQLException {
        return exec(sql, params, preparedStatement->{
            try {
                return preparedStatement.executeUpdate();
            } catch (Exception e) {
                e.printStackTrace();
            }

            return -1;
        });
    }

    @Override
    public int insert(String sql, List params) throws SQLException {
        return exec(sql, params, preparedStatement->{
            try {
                return preparedStatement.executeUpdate();
            } catch (SQLException e) {
                e.printStackTrace();
            }

            return -1;
        });
    }

    @Override
    public int delete(String sql, List params) throws SQLException {
        return exec(sql, params, preparedStatement->{
            try {
                return preparedStatement.executeUpdate();
            } catch (SQLException e) {
                e.printStackTrace();
            }

            return -1;
        });
    }

    protected <T> List<T> innerSearch(String sql, List params, Function<Map, T> cvn) throws SQLException {
        return exec(sql, params, preparedStatement->{
            try
            {
                ResultSet resultSet = preparedStatement.executeQuery();
                List<T> res = new ArrayList<>();
                while(resultSet.next()) {
                    Map<String, Object> row = new HashMap<>();
                    ResultSetMetaData metaData = resultSet.getMetaData();
                    for (int i=0;i<metaData.getColumnCount();i++) {
                        row.put(metaData.getColumnName(i + 1), resultSet.getObject(i + 1));
                    }
                    res.add(cvn.apply(row));
                }

                return res;
            } catch (Exception e) {
                e.printStackTrace();
            }

            return null;
        });
    }

    protected <T> T exec(String sql, List params, Function<PreparedStatement, T> sup) throws SQLException {
        try(Connection conn = dataSource.getConnection()) {
            try(PreparedStatement preparedStatement = conn.prepareStatement(sql)){
                for(int i=0;i<params.size();i++) {
                    preparedStatement.setObject(i + 1, params.get(i));
                }
                return sup.apply(preparedStatement);
            }
        }
    }
}
